<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			body{
				background-color: #4b4b4b;
			}
			#container{
				width: 600px;
				height: 600px;
			}
		</style>
	</head>
	<body>
		<div id="container">
		</div>
		<script type="text/javascript">
			!function (e,w) {
				function Pie (option) {
					Object.assign(this,{
						el: "#container",
						container: null,
						canvas: null,
						cx: null,
						timer: null,
						mTimer: null,
						drawList: [],
						ms: 0,
						mouse: [0,0],
						vMouse: [0,0],
						buoy: null,
						buoyVisible: false,
						option: {
							radius: 200,
							series: [
								{
									label: "标签1",
									value: 500,
								},
								{
									label: "标签2",
									value: 200,
								},
								{
									label: "标签3",
									value: 300,
								},
								{
									label: "标签4",
									value: 900,
								},
								{
									label: "标签5",
									value: 400,
								},
								{
									label: "标签6",
									value: 150,
								}
							]
						}
					},option)
					this.init()
				}
				Pie.prototype = {
					init(){
						let c = e.querySelector(this.el)
						if(!c) return
						this.mainWidth = c.offsetWidth
						this.mainHeight = c.offsetHeight
						c.style.position = "relative"
						this.container = c
						this.createCanvas()
						this.createBuoy()
						this.addEvents()
						this.initVMouse()
						this.start()
					},
					createCanvas(){
						let cContainer = e.createElement("div")
						let canvas = e.createElement("canvas")
						cContainer.style = `position:absolute;left:0;top:0;width:100%;height:100%`
						canvas.setAttribute("width",this.mainWidth)
						canvas.setAttribute("height",this.mainHeight)
						this.cx = canvas.getContext("2d")
						this.canvas = canvas
						this.container.appendChild(cContainer)
						cContainer.appendChild(canvas)
					},
					createBuoy(){
						let b = e.createElement("div")
						let n = e.createElement("div")
						let v = e.createElement("div")
						b.style = `
						position: absolute;
						left: 0;
						top: 0;
						z-index: 1;
						padding: 10px 60px;
						background-color: #ffffffbb;
						border-radius: 14px;
						border: 4px solid #99f;
						text-align: center;
						transform: translate(-50%,10px);
						box-shadow: 0 5px 5px rgba(0,0,0,.2);
						display: none;
						`
						n.style = `
						font-size: 18px;
						color: #333;
						line-height: 28px;
						`
						v.style = `
						color: #59587e;
						font-weight: bold;
						font-size: 30px;
						`
						this.container.appendChild(b)
						b.appendChild(n)
						b.appendChild(v)
						this.buoy = {b,n,v}
					},
					moveBuoy(name,value,pos,borderColor){
						this.buoy.b.style.display = this.buoyVisible ? "block" : "none"
						this.buoy.b.style.borderColor = borderColor
						this.buoy.b.style.left = pos[0] + "px"
						this.buoy.b.style.top = pos[1] + "px"
						this.buoy.n.innerHTML = name
						this.buoy.v.innerHTML = value
					},
					addEvents(){
						this.mousemove = evt => {
							this.mouse[0] = evt.pageX
							this.mouse[1] = evt.pageY
						}
						w.addEventListener("mousemove",this.mousemove)
					},
					initVMouse(){
						this.mTimer = setInterval(() => {
							this.vMouse[0] += (this.mouse[0] - this.vMouse[0]) / 10
							this.vMouse[1] += (this.mouse[1] - this.vMouse[1]) / 10
						},1000/60)
					},
					draw(frame,once){
						if(!this.canvas) return
						this.timer && clearInterval(this.timer)
						this.drawList.push({frame,once})
						if(this.drawList.every(obj => obj.once)){
							this.drawList.forEach(obj => obj.frame())
						}else{
							this.timer = setInterval(() => {
								this.ms = performance.now()
								this.cx.save()
								this.cx.clearRect(0,0,this.mainWidth,this.mainHeight)
								this.drawList.forEach(obj => obj.frame())
								this.cx.restore()
							},1000/60)
						}
					},
					start(){
						let sum = this.option.series.reduce((sum,el) => {
							el.color = `hsl(${Math.floor(Math.random() * 360)},70%,75%)`
							return sum + el.value
						},0)
						this.option.series.forEach(el => {
							el.val = (el.value / sum * 100).toFixed(1) + "%"
							el.a = el.value / sum * Math.PI * 2
						})
						let center = [this.mainWidth / 2, this.mainHeight / 2]
						let r = this.option.radius
						let add
						let actIndex
						let currInfo
						this.draw(() => {
							add = 0
							actIndex = -1
							this.cx.shadowBlur = 0
							this.option.series.forEach((s,i) => {
								this.cx.beginPath()
								this.cx.moveTo(...center)
								this.cx.arc(...center,r,add,add + s.a)
								this.cx.closePath()
								if(this.cx.isPointInPath(...this.mouse)){ // 鼠标hover 时
									actIndex = i
									currInfo = [add,add + s.a]
								}
								this.cx.fillStyle = s.color
								this.cx.fill()
								add += s.a
							})
							// 如果鼠标正在 hover
							if(actIndex !== -1){
								this.buoyVisible = true
								let curr = this.option.series[actIndex]
								this.cx.beginPath()
								this.cx.moveTo(...center)
								this.cx.arc(...center,r,...currInfo)
								this.cx.closePath()
								// 一层阴影
								this.cx.shadowBlur = 10
								this.cx.shadowColor = "#00000055"
								this.cx.fill()
								// 两层阴影
								this.cx.shadowBlur = 20
								this.cx.shadowColor = "#00000033"
								this.cx.fill()
								// 激活扇形覆盖层
								this.cx.shadowBlur = 0
								this.cx.fillStyle = curr.color
								this.cx.fill()
								this.cx.fillStyle = "#ffffff55"
								this.cx.fill()
								// 操作 dom
								this.moveBuoy(curr.label,curr.val,this.vMouse,curr.color)
							}else{
								this.buoyVisible = false
								this.moveBuoy(1,1,this.vMouse)
							}
							
						})
					}
				}
				w.Pie = Pie
			}(document,window)
			const pie = new Pie()
		</script>
	</body>
</html>